home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Resources / Developers / XAMPP 1.5.4 / Windows installer / xampp-win32-1.5.4-installer.exe / xampp / php / pear / Math / Finance.php < prev    next >
Encoding:
PHP Script  |  2006-04-07  |  31.6 KB  |  874 lines

  1. <?php
  2.  
  3. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  4.  
  5. /**
  6.  * Math_Finance: Class of financial functions
  7.  *
  8.  * Assorted financial functions for interest rates, bonds, amortizations and time value of money calculations (annuities)
  9.  * Same interface as Excel financial functions.
  10.  *
  11.  * PHP versions 4 and 5
  12.  *
  13.  * LICENSE: This source file is subject to version 3.0 of the PHP license
  14.  * that is available through the world-wide-web at the following URI:
  15.  * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
  16.  * the PHP License and are unable to obtain it through the web, please
  17.  * send a note to license@php.net so we can mail you a copy immediately.
  18.  *
  19.  * @category   Math
  20.  * @package    Math_Finance
  21.  * @author     Original Author <alejandro.pedraza@dataenlace.com>
  22.  * @copyright  2005 Alejandro Pedraza
  23.  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  24.  * @version    CVS: $Id:$
  25.  * @link       http://pear.php.net/Math/Finance
  26.  * @since      File available since Release 1.2.0
  27.  */
  28.  
  29. // to be able to throw PEAR errors
  30. require_once 'PEAR.php';
  31.  
  32. // precision of calculations
  33. define('FINANCE_PRECISION', 1E-6);
  34.  
  35. // payment types
  36. define('FINANCE_PAY_END', 0);
  37. define('FINANCE_PAY_BEGIN', 1);
  38.  
  39. // types of daycount basis
  40. define('FINANCE_COUNT_NASD', 0);
  41. define('FINANCE_COUNT_ACTUAL_ACTUAL', 1);
  42. define('FINANCE_COUNT_ACTUAL_360', 2);
  43. define('FINANCE_COUNT_ACTUAL_365', 3);
  44. define('FINANCE_COUNT_EUROPEAN', 4);
  45.  
  46. /**
  47.  * Math_Finance: Main class
  48.  *
  49.  * @category   Math
  50.  * @package    Math_Finance
  51.  * @author     Original Author <alejandro.pedraza@dataenlace.com>
  52.  * @copyright  2005 Alejandro Pedraza
  53.  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  54.  * @version    Release: @package_version@
  55.  * @link       http://pear.php.net/Math/Finance
  56.  * @since      Class available since Release 1.2.0
  57.  */
  58. class Math_Finance
  59. {
  60.     /*******************************************************************
  61.     ** Interest Rates Conversion Functions                         *****
  62.     *******************************************************************/
  63.  
  64.     /**
  65.     * Returns the effective interest rate given the nominal rate and the number of compounding payments per year
  66.     * Excel equivalent: EFFECT
  67.     *
  68.     * @param float      Nominal interest rate
  69.     * @param int        Number of compounding payments per year
  70.     * @return float     
  71.     * @static
  72.     * @access public
  73.     */
  74.     function effectiveRate($nominal_rate, $npery)
  75.     {
  76.         $npery = (int)$npery;
  77.         if ($npery < 0) {
  78.             return PEAR::raiseError('Number of compounding payments per year is not positive');
  79.         }
  80.  
  81.         $effect = pow((1 + $nominal_rate / $npery), $npery) - 1;
  82.         return $effect;
  83.     }
  84.  
  85.     /**
  86.     * Returns the nominal interest rate given the effective rate and the number of compounding payments per year
  87.     * Excel equivalent: NOMINAL
  88.     *
  89.     * @param float      Effective interest rate
  90.     * @param int        Number of compounding payments per year
  91.     * @return float     
  92.     * @static
  93.     * @access public
  94.     */
  95.     function nominalRate($effect_rate, $npery)
  96.     {
  97.         $npery = (int)$npery;
  98.         if ($npery < 0) {
  99.             return PEAR::raiseError('Number of compounding payments per year is not positive');
  100.         }
  101.  
  102.         $nominal = $npery * (pow($effect_rate + 1, 1/$npery) - 1);
  103.         return $nominal;
  104.     }
  105.  
  106.  
  107.     /*******************************************************************
  108.     ** TVM (annuities) Functions                                   *****
  109.     *******************************************************************/
  110.  
  111.     /**
  112.     * Returns the Present Value of a cash flow with constant payments and interest rate (annuities)
  113.     * Excel equivalent: PV
  114.     *
  115.     *   TVM functions solve for a term in the following formula:
  116.     *   pv(1+r)^n + pmt(1+r.type)((1+r)^n - 1)/r) +fv = 0
  117.     *
  118.     *
  119.     * @param float      Interest rate per period 
  120.     * @param int        Number of periods
  121.     * @param float      Periodic payment (annuity)
  122.     * @param float      Future Value
  123.     * @param int        Payment type:
  124.                             FINANCE_PAY_END (default):    at the end of each period
  125.                             FINANCE_PAY_BEGIN:            at the beginning of each period
  126.     * @return float     
  127.     * @static
  128.     * @access public
  129.     */
  130.     function presentValue($rate, $nper, $pmt, $fv = 0, $type = 0)
  131.     {
  132.         if ($nper < 0) {
  133.             return PEAR::raiseError('Number of periods must be positive');
  134.         }
  135.         if ($type != FINANCE_PAY_END && $type != FINANCE_PAY_BEGIN) {
  136.             return PEAR::raiseError('Payment type must be FINANCE_PAY_END or FINANCE_PAY_BEGIN');
  137.         }
  138.  
  139.         if ($rate) {
  140.             $pv = (-$pmt * (1 + $rate * $type) * ((pow(1 + $rate, $nper) - 1) / $rate) - $fv) / pow(1 + $rate, $nper);
  141.         } else {
  142.             $pv = -$fv - $pmt * $nper;
  143.         }
  144.         return $pv;
  145.     }
  146.  
  147.     /**
  148.     * Returns the Future Value of a cash flow with constant payments and interest rate (annuities)
  149.     * Excel equivalent: FV
  150.     *
  151.     * @param float      Interest rate per period 
  152.     * @param int        Number of periods
  153.     * @param float      Periodic payment (annuity)
  154.     * @param float      Present Value
  155.     * @param int        Payment type:
  156.                             FINANCE_PAY_END (default):    at the end of each period
  157.                             FINANCE_PAY_BEGIN:            at the beginning of each period
  158.     * @return float     
  159.     * @static
  160.     * @access public
  161.     */
  162.     function futureValue($rate, $nper, $pmt, $pv = 0, $type = 0)
  163.     {
  164.         if ($nper < 0) {
  165.             return PEAR::raiseError('Number of periods must be positive');
  166.         }
  167.         if ($type != FINANCE_PAY_END && $type != FINANCE_PAY_BEGIN) {
  168.             return PEAR::raiseError('Payment type must be FINANCE_PAY_END or FINANCE_PAY_BEGIN');
  169.         }
  170.  
  171.         if ($rate) {
  172.             $fv = -$pv * pow(1 + $rate, $nper) - $pmt * (1 + $rate * $type) * (pow(1 + $rate, $nper) - 1) / $rate;
  173.         } else {
  174.             $fv = -$pv - $pmt * $nper;
  175.         }
  176.         return $fv;
  177.     }
  178.  
  179.     /**
  180.     * Returns the constant payment (annuity) for a cash flow with a constant interest rate
  181.     * Excel equivalent: PMT
  182.     *
  183.     * @param float      Interest rate per period 
  184.     * @param int        Number of periods
  185.     * @param float      Present Value
  186.     * @param float      Future Value
  187.     * @param int        Payment type:
  188.                             FINANCE_PAY_END (default):    at the end of each period
  189.                             FINANCE_PAY_BEGIN:            at the beginning of each period
  190.     * @return float     
  191.     * @static
  192.     * @access public
  193.     */
  194.     function payment($rate, $nper, $pv, $fv = 0, $type = 0)
  195.     {
  196.         if ($nper < 0) {
  197.             return PEAR::raiseError('Number of periods must be positive');
  198.         }
  199.         if ($type != FINANCE_PAY_END && $type != FINANCE_PAY_BEGIN) {
  200.             return PEAR::raiseError('Payment type must be FINANCE_PAY_END or FINANCE_PAY_BEGIN');
  201.         }
  202.  
  203.         if ($rate) {
  204.             $pmt = (-$fv - $pv * pow(1 + $rate, $nper)) / (1 + $rate * $type) / ((pow(1 + $rate, $nper) - 1) / $rate);
  205.         } else {
  206.             $pmt = (-$pv - $fv) / $nper;
  207.         }
  208.         return $pmt;
  209.     }
  210.  
  211.     /**
  212.     * Returns the number of periods for a cash flow with constant periodic payments (annuities), and interest rate
  213.     * Excel equivalent: NPER
  214.     *
  215.     * @param float      Interest rate per period 
  216.     * @param float      Periodic payment (annuity)
  217.     * @param float      Present Value
  218.     * @param float      Future Value
  219.     * @param int        Payment type:
  220.                             FINANCE_PAY_END (default):    at the end of each period
  221.                             FINANCE_PAY_BEGIN:            at the beginning of each period
  222.     * @return float
  223.     * @static
  224.     * @access public
  225.     */
  226.     function periods($rate, $pmt, $pv, $fv = 0, $type = 0)
  227.     {
  228.         if ($type != FINANCE_PAY_END && $type != FINANCE_PAY_BEGIN) {
  229.             return PEAR::raiseError('Payment type must be FINANCE_PAY_END or FINANCE_PAY_BEGIN');
  230.         }
  231.  
  232.         if ($rate) {
  233.             if ($pmt == 0 && $pv == 0) {
  234.                 return PEAR::raiseError('Payment and Present Value can\'t be both zero when the rate is not zero');
  235.             }
  236.             $nper = log(($pmt * (1 + $rate * $type) / $rate - $fv) / ($pv + $pmt * (1 + $rate * $type) / $rate))
  237.                      / log(1 + $rate);
  238.         } else {
  239.             if ($pmt == 0) {
  240.                 return PEAR::raiseError('Rate and Payment can\'t be both zero');
  241.             }
  242.             $nper = (-$pv -$fv) / $pmt;
  243.         }
  244.         return $nper;
  245.     }
  246.  
  247.     /**
  248.     * Returns the periodic interest rate for a cash flow with constant periodic payments (annuities)
  249.     * Excel equivalent: RATE
  250.     *
  251.     * @param int        Number of periods
  252.     * @param float      Periodic payment (annuity)
  253.     * @param float      Present Value
  254.     * @param float      Future Value
  255.     * @param int        Payment type:
  256.                             FINANCE_PAY_END (default):    at the end of each period
  257.                             FINANCE_PAY_BEGIN:            at the beginning of each period
  258.     * @param float      guess for the interest rate
  259.     * @return float     
  260.     * @static
  261.     * @access public
  262.     */
  263.     function rate($nper, $pmt, $pv, $fv = 0, $type = 0, $guess = 0.1)
  264.     {
  265.         // To solve the equation
  266.         require_once 'Math/Numerical/RootFinding/NewtonRaphson.php';
  267.         // To preserve some variables in the Newton-Raphson callback functions
  268.         require_once 'Math/Finance_FunctionParameters.php';
  269.  
  270.         if ($type != FINANCE_PAY_END && $type != FINANCE_PAY_BEGIN) {
  271.             return PEAR::raiseError('Payment type must be FINANCE_PAY_END or FINANCE_PAY_BEGIN');
  272.         }
  273.  
  274.         // Utilization of a Singleton class to preserve given values of other variables in the callback functions
  275.         $parameters = array(
  276.             'nper'  => $nper,
  277.             'pmt'   => $pmt,
  278.             'pv'    => $pv,
  279.             'fv'    => $fv,
  280.             'type'  => $type,
  281.         );
  282.         $parameters_class =& Math_Finance_FunctionParameters::getInstance($parameters, True);
  283.  
  284.         $newtonRaphson = new Math_Numerical_RootFinding_Newtonraphson(array('err_tolerance' => FINANCE_PRECISION));
  285.         return $newtonRaphson->compute(array('Math_Finance', '_tvm'), array('Math_Finance', '_dtvm'), $guess);
  286.     }
  287.  
  288.     /**
  289.     * Callback function only used by Newton-Raphson algorithm. Returns value of function to be solved.
  290.     *
  291.     * Uses a previously instanced Singleton class to retrieve given values of other variables in the function
  292.     *
  293.     * @param float      Interest rate
  294.     * @return float     
  295.     * @static
  296.     * @access private
  297.     */
  298.     function _tvm($rate)
  299.     {
  300.         require_once 'Math/Finance_FunctionParameters.php';
  301.  
  302.         $parameters_class =& Math_Finance_FunctionParameters::getInstance();
  303.         $nper   = $parameters_class->parameters['nper'];
  304.         $pmt    = $parameters_class->parameters['pmt'];
  305.         $pv     = $parameters_class->parameters['pv'];
  306.         $fv     = $parameters_class->parameters['fv'];
  307.         $type   = $parameters_class->parameters['type'];
  308.  
  309.         return $pv * pow(1 + $rate, $nper) + $pmt * (1 + $rate * $type) * (pow(1 + $rate, $nper) - 1) / $rate + $fv;
  310.     }
  311.  
  312.     /**
  313.     * Callback function only used by Newton-Raphson algorithm. Returns value of derivative of function to be solved.
  314.     *
  315.     * Uses a previously instanced Singleton class to retrieve given values of other variables in the function
  316.     *
  317.     * @return float     
  318.     * @static
  319.     * @access private
  320.     */
  321.     function _dtvm($rate)
  322.     {
  323.         require_once 'Math/Finance_FunctionParameters.php';
  324.  
  325.         $parameters_class =& Math_Finance_FunctionParameters::getInstance();
  326.         $nper   = $parameters_class->parameters['nper'];
  327.         $pmt    = $parameters_class->parameters['pmt'];
  328.         $pv     = $parameters_class->parameters['pv'];
  329.         $type   = $parameters_class->parameters['type'];
  330.  
  331.         return $nper * $pv * pow(1 + $rate, $nper - 1)
  332.                  + $pmt *
  333.                      ($type * (pow(1 + $rate, $nper) - 1) / $rate
  334.                      + (1 + $rate * $type) * ($nper * $rate * pow(1 + $rate, $nper - 1) - pow(1 + $rate, $nper) + 1) / pow($rate,2));
  335.     }
  336.  
  337.     /**
  338.     * Returns the interest payment for a given period for a cash flow with constant periodic payments (annuities)
  339.     * and interest rate.
  340.     * Excel equivalent: IMPT
  341.     *
  342.     * @param float      Interest rate per period 
  343.     * @param int        Period for which the interest payment will be calculated
  344.     * @param int        Number of periods
  345.     * @param float      Present Value
  346.     * @param float      Future Value
  347.     * @param int        Payment type:
  348.                             FINANCE_PAY_END (default):    at the end of each period
  349.                             FINANCE_PAY_BEGIN:            at the beginning of each period
  350.     * @return float     
  351.     * @static
  352.     * @access public
  353.     */
  354.     function interestPayment($rate, $per, $nper, $pv, $fv = 0, $type = 0)
  355.     {
  356.         if ($type != FINANCE_PAY_END && $type != FINANCE_PAY_BEGIN) {
  357.             return PEAR::raiseError('Payment type must be FINANCE_PAY_END or FINANCE_PAY_BEGIN');
  358.         }
  359.  
  360.         $interestAndPrincipal = Math_Finance::_interestAndPrincipal($rate, $per, $nper, $pv, $fv, $type);
  361.         return $interestAndPrincipal[0];
  362.     }
  363.  
  364.     /**
  365.     * Returns the principal payment for a given period for a cash flow with constant periodic payments (annuities)
  366.     * and interest rate
  367.     * Excel equivalent: PPMT
  368.     *
  369.     * @param float      Interest rate per period 
  370.     * @param int        Period for which the principal payment will be calculated
  371.     * @param int        Number of periods
  372.     * @param float      Present Value
  373.     * @param float      Future Value
  374.     * @param int        Payment type:
  375.                             FINANCE_PAY_END (default):    at the end of each period
  376.                             FINANCE_PAY_BEGIN:            at the beginning of each period
  377.     * @return float     
  378.     * @static
  379.     * @access public
  380.     */
  381.     function principalPayment($rate, $per, $nper, $pv, $fv = 0, $type = 0)
  382.     {
  383.         if ($type != FINANCE_PAY_END && $type != FINANCE_PAY_BEGIN) {
  384.             return PEAR::raiseError('Payment type must be FINANCE_PAY_END or FINANCE_PAY_BEGIN');
  385.         }
  386.  
  387.         $interestAndPrincipal = Math_Finance::_interestAndPrincipal($rate, $per, $nper, $pv, $fv, $type);
  388.         return $interestAndPrincipal[1];
  389.     }
  390.  
  391.     /**
  392.     * Returns the interest and principal payment for a given period for a cash flow with constant 
  393.     * periodic payments (annuities) and interest rate
  394.     *
  395.     * @param float      Interest rate per period 
  396.     * @param int        Number of periods
  397.     * @param float      Present Value
  398.     * @param float      Future Value
  399.     * @param int        Payment type:
  400.                             FINANCE_PAY_END (default):    at the end of each period
  401.                             FINANCE_PAY_BEGIN:            at the beginning of each period
  402.     * @return array
  403.     * @static
  404.     * @access private
  405.     */
  406.     function _interestAndPrincipal($rate, $per, $nper, $pv, $fv, $type)
  407.     {
  408.         $pmt = Math_Finance::payment($rate, $nper, $pv, $fv, $type);
  409.         //echo "pmt: $pmt\n\n";
  410.         $capital = $pv;
  411.         for ($i = 1; $i<= $per; $i++) {
  412.             // in first period of advanced payments no interests are paid
  413.             $interest = ($type && $i == 1)? 0 : -$capital * $rate;
  414.             $principal = $pmt - $interest;
  415.             $capital += $principal;
  416.             //echo "$i\t$capital\t$interest\t$principal\n";
  417.         }
  418.         return array($interest, $principal);
  419.     }
  420.  
  421.     /*******************************************************************
  422.     ** Cash Flow Functions                                        *****
  423.     *******************************************************************/
  424.  
  425.     /**
  426.     * Returns the Net Present Value of a cash flow series given a discount rate
  427.     * Excel equivalent: NPV
  428.     *
  429.     * @param float      Discount interest rate
  430.     * @param array      Cash flow series
  431.     * @return float     
  432.     * @static
  433.     * @access public
  434.     */
  435.  
  436.     function netPresentValue($rate, $values)
  437.     {
  438.         if (!is_array($values)) {
  439.             return PEAR::raiseError('The cash flow series most be an array');
  440.         }
  441.     
  442.         return MATH_Finance::_npv($rate, $values);
  443.     }
  444.  
  445.     /**
  446.     * Returns the internal rate of return of a cash flow series
  447.     * Excel equivalent: IRR
  448.     *
  449.     * @param array      Cash flow series
  450.     * @param float      guess for the interest rate
  451.     * @return float     
  452.     * @static
  453.     * @access public
  454.     */
  455.     function internalRateOfReturn($values, $guess = 0.1)
  456.     {
  457.         // To solve the equation
  458.         require_once 'Math/Numerical/RootFinding/NewtonRaphson.php';
  459.         // To preserve some variables in the Newton-Raphson callback functions
  460.         require_once 'Math/Finance_FunctionParameters.php';
  461.  
  462.         if (!is_array($values)) {
  463.             return PEAR::raiseError('The cash flow series most be an array');
  464.         }
  465.         if (min($values) * max($values) >= 0) {
  466.             return PEAR::raiseError('Cash flow must contain at least one positive value and one negative value');
  467.         }
  468.  
  469.         $parameters_class =& Math_Finance_FunctionParameters::getInstance(array('values' => $values), True);
  470.         $newtonRaphson = new Math_Numerical_RootFinding_Newtonraphson(array('err_tolerance' => FINANCE_PRECISION));
  471.         return $newtonRaphson->compute(array('Math_Finance', '_npv'), array('Math_Finance', '_dnpv'), $guess);
  472.     }
  473.  
  474.     /**
  475.     * Function used by NPV() and as a callback by Newton-Raphson algorithm.
  476.     * Returns value of Net Present Value of a cash flow series.
  477.     *
  478.     * Uses a previously instanced Singleton class to retrieve given values of other variables in the function
  479.     *
  480.     * @param float      Discount interest rate
  481.     * @param array      Cash flow series
  482.     * @return float     
  483.     * @static
  484.     * @access private
  485.     */
  486.     function _npv($rate, $values = array())
  487.     {
  488.         require_once 'Math/Finance_FunctionParameters.php';
  489.  
  490.         if (!$values) {
  491.             // called from IRR
  492.             $parameters_class =& Math_Finance_FunctionParameters::getInstance();
  493.             $values = $parameters_class->parameters['values'];
  494.         }
  495.  
  496.         $npv = 0;
  497.         $nper = count($values);
  498.         for ($i = 1; $i <= $nper; $i++) {
  499.             $npv += $values[$i-1]/ pow(1 + $rate, $i);
  500.         }
  501.         return $npv;
  502.     }
  503.  
  504.     /**
  505.     * Callback function used by by Newton-Raphson algorithm to calculate IRR.
  506.     * Returns value of derivative function to be solved.
  507.     *
  508.     * Uses a previously instanced Singleton class to retrieve given values of other variables in the function
  509.     *
  510.     * @param float      Discount interest rate
  511.     * @param array      Cash flow series
  512.     * @return float     
  513.     * @static
  514.     * @access private
  515.     */
  516.     function _dnpv($rate, $values = array())
  517.     {
  518.         require_once 'Math/Finance_FunctionParameters.php';
  519.  
  520.         if (!$values) {
  521.             // called from IRR
  522.             $parameters_class =& Math_Finance_FunctionParameters::getInstance();
  523.             $values = $parameters_class->parameters['values'];
  524.         }
  525.  
  526.         $dnpv = 0;
  527.         $nper = count($values);
  528.         for ($i = 1; $i <= $nper; $i++) {
  529.             $dnpv += $values[$i-1] * (-$i) * pow(1 + $rate, $i - 1) / pow(1 + $rate, 2 * $i);
  530.         }
  531.         return $dnpv;
  532.     }
  533.  
  534.     /**
  535.     * Returns the internal rate of return of a cash flow series, considering both financial and reinvestment rates
  536.     * Excel equivalent: MIRR
  537.     *
  538.     * @param array      Cash flow series
  539.     * @param float      Interest rate on the money used in the cash flow
  540.     * @param float      Interest rate received when reinvested
  541.     * @return float     
  542.     * @static
  543.     * @access public
  544.     */
  545.     function modifiedInternalRateOfReturn($values, $finance_rate, $reinvest_rate)
  546.     {
  547.         if (!is_array($values)) {
  548.             return PEAR::raiseError('The cash flow series most be an array');
  549.         }
  550.         if (min($values) * max($values) >= 0) {
  551.             return PEAR::raiseError('Cash flow must contain at least one positive value and one negative value');
  552.         }
  553.  
  554.         $positive_flows = $negative_flows = array();
  555.         foreach ($values as $value) {
  556.             if ($value >= 0) {
  557.                 $positive_flows[] = $value;
  558.                 $negative_flows[] = 0;
  559.             } else {
  560.                 $positive_flows[] = 0;
  561.                 $negative_flows[] = $value;
  562.             }
  563.         }
  564.  
  565.         $nper = count($values);
  566.  
  567.         return pow(-Math_Finance::netPresentValue($reinvest_rate, $positive_flows) * pow(1 + $reinvest_rate, $nper)
  568.                 / Math_Finance::netPresentValue($finance_rate, $negative_flows) / (1 + $finance_rate), 1/($nper - 1)) - 1;
  569.     }
  570.  
  571.     /*******************************************************************
  572.     ** Bonds Functions                                             *****
  573.     *******************************************************************/
  574.  
  575.     /**
  576.     * Returns the difference of days between two dates based on a daycount basis
  577.     *
  578.     * @param int        First date (UNIX timestamp)
  579.     * @param int        Second date (UNIX timestamp)
  580.     * @param int        Type of day count basis:
  581.                             FINANCE_COUNT_NASD(default):    US(NASD) 30/360
  582.                             FINANCE_COUNT_ACTUAL_ACTUAL:    Actual/actual
  583.                             FINANCE_COUNT_ACTUAL_360:       Actual/360
  584.                             FINANCE_COUNT_ACTUAL_365:       Actual/365
  585.                             FINANCE_COUNT_EUROPEAN:         European 30/360
  586.     * @return int
  587.     * @static
  588.     * @access public
  589.     */
  590.     function daysDifference($date1, $date2, $basis)
  591.     {
  592.         $y1 = date('Y', $date1);
  593.         $m1 = date('n', $date1);
  594.         $d1 = date('j', $date1);
  595.         $y2 = date('Y', $date2);
  596.         $m2 = date('n', $date2);
  597.         $d2 = date('j', $date2);
  598.  
  599.         switch ($basis) {
  600.             case FINANCE_COUNT_NASD:
  601.                 if ($d2 == 31 && ($d1 == 30 || $d1 == 31)) {
  602.                     $d2 = 30;
  603.                 }
  604.                 if ($d1 == 31) {
  605.                     $d1 = 30;
  606.                 }
  607.                 return ($y2 - $y1) * 360 + ($m2 - $m1) * 30 + $d2 - $d1;
  608.             case FINANCE_COUNT_ACTUAL_ACTUAL:
  609.             case FINANCE_COUNT_ACTUAL_360:
  610.             case FINANCE_COUNT_ACTUAL_365:
  611.                 return ($date2 - $date1) / 86400;
  612.             case FINANCE_COUNT_EUROPEAN: // European 30/360
  613.                 return ($y2 - $y1) * 360 + ($m2 - $m1) * 30 + $d2 - $d1;
  614.         }
  615.     }
  616.  
  617.     /**
  618.     * Returns the number of days in the year based on a daycount basis
  619.     *
  620.     * @param int        Year
  621.     * @param int        Type of day count basis:
  622.                             FINANCE_COUNT_NASD(default):    US(NASD) 30/360
  623.                             FINANCE_COUNT_ACTUAL_ACTUAL:    Actual/actual
  624.                             FINANCE_COUNT_ACTUAL_360:       Actual/360
  625.                             FINANCE_COUNT_ACTUAL_365:       Actual/365
  626.                             FINANCE_COUNT_EUROPEAN:         European 30/360
  627.     * @return int
  628.     * @static
  629.     * @access public
  630.     */
  631.     function daysPerYear($year, $basis)
  632.     {
  633.         switch ($basis) {
  634.             case FINANCE_COUNT_NASD:
  635.                 return 360;
  636.             case FINANCE_COUNT_ACTUAL_ACTUAL:
  637.                 return checkdate(2, 29, $year)? 366 : 365;
  638.             case FINANCE_COUNT_ACTUAL_360:
  639.                 return 360;
  640.             case FINANCE_COUNT_ACTUAL_365:
  641.                 return 365;
  642.             case FINANCE_COUNT_EUROPEAN:
  643.                 return 360;
  644.         }
  645.     }
  646.  
  647.     /**
  648.     * Returns the yield for a treasury bill
  649.     * Excel equivalent: TBILLYIELD
  650.     *
  651.     * @param int        Settlement date (UNIX timestamp)
  652.     * @param int        Maturity date (UNIX timestamp)
  653.     * @param float      TBill price per $100 face value
  654.     * @return float     
  655.     * @static
  656.     * @access public
  657.     */
  658.     function TBillYield($settlement, $maturity, $pr)
  659.     {
  660.         if ($settlement >= $maturity) {
  661.             return PEAR::raiseError('Maturity must happen before settlement!');
  662.         }
  663.  
  664.         $dsm = ($maturity - $settlement) / 86400;   // transform to days
  665.  
  666.         if ($dsm > 360) {
  667.             return PEAR::raiseError("maturity can't be more than one year after settlement");
  668.         }
  669.  
  670.         return (100 - $pr) * 360 / $pr / $dsm;
  671.     }
  672.  
  673.     /**
  674.     * Returns the price per $100 face value for a Treasury bill
  675.     * Excel equivalent: TBILLPRICE
  676.     *
  677.     * @param int        Settlement date (UNIX timestamp)
  678.     * @param int        Maturity date (UNIX timestamp)
  679.     * @param float      T-Bill discount rate
  680.     * @return float     
  681.     * @static
  682.     * @access public
  683.     */
  684.     function TBillPrice($settlement, $maturity, $discount)
  685.     {
  686.         if ($settlement >= $maturity) {
  687.             return PEAR::raiseError('Maturity must happen before settlement!');
  688.         }
  689.  
  690.         $dsm = ($maturity - $settlement) / 86400;   // transform to days
  691.  
  692.         if ($dsm > 360) {
  693.             return PEAR::raiseError("maturity can't be more than one year after settlement");
  694.         }
  695.  
  696.         return 100 * (1 - $discount * $dsm / 360);
  697.     }
  698.  
  699.     /**
  700.     * Returns the bond-equivalent yield for a Treasury bill
  701.     * Excel equivalent: TBILLEQ
  702.     *
  703.     * @param int        Settlement date (UNIX timestamp)
  704.     * @param int        Maturity date (UNIX timestamp)
  705.     * @param float      T-Bill discount rate
  706.     * @return float     
  707.     * @static
  708.     * @access public
  709.     */
  710.     function TBillEquivalentYield($settlement, $maturity, $discount)
  711.     {
  712.         if ($settlement >= $maturity) {
  713.             return PEAR::raiseError('Maturity must happen before settlement!');
  714.         }
  715.  
  716.         $dsm = Math_Finance::daysDifference($settlement, $maturity, FINANCE_COUNT_ACTUAL_365);
  717.  
  718.         if ($dsm <= 182) {
  719.             // for one half year or less, the bond-equivalent-yield is equivalent to an actual/365 interest rate
  720.             return 365 * $discount / (360 - $discount * $dsm);
  721.         } elseif ($dsm == 366 
  722.                   && ((date('m', $settlement) <= 2 && checkdate(2, 29, date('Y', $settlement))) 
  723.                   || (date('m', $settlement) > 2 && checkdate(2, 29, date('Y', $maturity))))) {
  724.             return 2 * (sqrt(1 - $discount * 366 / ($discount * 366 - 360)) - 1);
  725.         } elseif ($dsm > 365) {
  726.             return PEAR::raiseError("maturity can't be more than one year after settlement");
  727.         } else {
  728.             // thanks to Zhang Qingpo (zhangqingpo@yahoo.com.cn) for solving this riddle :)
  729.             return (-$dsm + sqrt(pow($dsm, 2) - (2 * $dsm - 365) * $discount * $dsm * 365 / ($discount * $dsm - 360))) / ($dsm - 365 / 2);
  730.         }
  731.     }
  732.  
  733.     /**
  734.     * Returns the discount rate for a bond
  735.     * Excel equivalent: DISC
  736.     *
  737.     * @param int        Settlement date (UNIX timestamp)
  738.     * @param int        Maturity date (UNIX timestamp)
  739.     * @param float      The bond's price per $100 face value
  740.     * @param float      The bond's redemption value per $100 face value
  741.     * @param int        Type of day count basis:
  742.                             FINANCE_COUNT_NASD(default):    US(NASD) 30/360
  743.                             FINANCE_COUNT_ACTUAL_ACTUAL:    Actual/actual
  744.                             FINANCE_COUNT_ACTUAL_360:       Actual/360
  745.                             FINANCE_COUNT_ACTUAL_365:       Actual/365
  746.                             FINANCE_COUNT_EUROPEAN:         European 30/360
  747.     * @return float     
  748.     * @static
  749.     * @access public
  750.     */
  751.     function discountRate($settlement, $maturity, $pr, $redemption, $basis = 0)
  752.     {
  753.         $days_per_year = Math_Finance::daysPerYear(date('Y', $settlement), $basis);
  754.         $dsm = Math_Finance::daysDifference($settlement, $maturity, $basis);
  755.  
  756.         return ($redemption - $pr) * $days_per_year / $redemption / $dsm;
  757.     }
  758.  
  759.     /**
  760.     * Returns the price per $100 face value of a discounted bond
  761.     * Excel equivalent: PRICEDISC
  762.     *
  763.     * @param int        Settlement date (UNIX timestamp)
  764.     * @param int        Maturity date (UNIX timestamp)
  765.     * @param float      The bond's discount rate
  766.     * @param float      The bond's redemption value per $100 face value
  767.     * @param int        Type of day count basis:
  768.                             FINANCE_COUNT_NASD(default):    US(NASD) 30/360
  769.                             FINANCE_COUNT_ACTUAL_ACTUAL:    Actual/actual
  770.                             FINANCE_COUNT_ACTUAL_360:       Actual/360
  771.                             FINANCE_COUNT_ACTUAL_365:       Actual/365
  772.                             FINANCE_COUNT_EUROPEAN:         European 30/360
  773.     * @return float     
  774.     * @static
  775.     * @access public
  776.     */
  777.     function priceDiscount($settlement, $maturity, $discount, $redemption, $basis = 0)
  778.     {
  779.         $days_per_year = Math_Finance::daysPerYear(date('Y', $settlement), $basis);
  780.         $dsm = Math_Finance::daysDifference($settlement, $maturity, $basis);
  781.  
  782.         return $redemption - $discount * $redemption * $dsm / $days_per_year;
  783.     }
  784.  
  785.  
  786.     /*******************************************************************
  787.     ** Depreciation Functions                                      *****
  788.     *******************************************************************/
  789.  
  790.     /**
  791.     * Returns the depreciation of an asset using the fixed-declining balance method
  792.     * Excel equivalent: DB
  793.     *
  794.     * @param float      The initial cost of the asset
  795.     * @param float      Salvage value of the asset
  796.     * @param int        Number of depreciation periods (same unit as $life)
  797.     * @param int        Number of months in the first year, defaults to 12
  798.     * @return float     
  799.     * @static
  800.     * @access public
  801.     */
  802.     function depreciationFixedDeclining($cost, $salvage, $life, $period, $month = 12)
  803.     {
  804.         $cost       = (float) $cost;
  805.         $salvage    = (float) $salvage;
  806.         $life       = (int) $life;
  807.         $period     = (int) $period;
  808.         $month      = (int) $month;
  809.         if ($cost < 0 || $life < 0) {
  810.             return PEAR::raiseError('cost and life must be absolute positive numbers');
  811.         }
  812.         if ($period < 1) {
  813.             return PEAR::raiseError('period must be greater or equal than one');
  814.         }
  815.  
  816.         $rate = 1 - pow(($salvage / $cost), (1 / $life));
  817.         $rate = round($rate, 3);
  818.  
  819.         $acc_depreciation = 0;
  820.         for ($i = 1; $i <= $period; $i++) {
  821.             if ($i == 1) {
  822.                 $depreciation_period = $cost * $rate * $month / 12;
  823.             } elseif ($i == ($life + 1)) {
  824.                 $depreciation_period = ($cost - $acc_depreciation) * $rate * (12 - $month) / 12;
  825.             } else {
  826.                 $depreciation_period = ($cost - $acc_depreciation) * $rate;
  827.             }
  828.             $acc_depreciation += $depreciation_period;
  829.         }
  830.  
  831.         return $depreciation_period;
  832.     }
  833.  
  834.     /**
  835.     * Returns the straight-line depreciation of an asset for each period
  836.     * Excel equivalent: SLN
  837.     *
  838.     * @param float      The initial cost of the asset
  839.     * @param float      Salvage value of the asset
  840.     * @param int        Number of depreciation periods
  841.     * @return float     
  842.     * @static
  843.     * @access public
  844.     */
  845.     function depreciationStraightLine($cost, $salvage, $life)
  846.     {
  847.         $life       = (int) $life;
  848.         if ($cost < 0 || $life < 0) {
  849.             return PEAR::raiseError('cost and life must be absolute positive numbers');
  850.         }
  851.  
  852.         return (($cost - $salvage) / $life);
  853.     }
  854.  
  855.     /**
  856.     * Returns the depreciation for an asset in a given period using the sum-of-years' digits method
  857.     * Excel equivalent: SYD
  858.     *
  859.     * @param float      The initial cost of the asset
  860.     * @param float      Salvage value of the asset
  861.     * @param int        Number of depreciation periods
  862.     * @param int        Period (must be in the same unit as $life)
  863.     * @return float     
  864.     * @static
  865.     * @access public
  866.     */
  867.     function depreciationSYD($cost, $salvage, $life, $per)
  868.     {
  869.         return (($cost - $salvage) * ($life - $per + 1) * 2 / ($life) / ($life +1));
  870.     }
  871. }
  872.  
  873. ?>
  874.